#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <openssl/evp.h>
#include "randgen.h"

#define RAND_DEV "/dev/urandom"

struct _RAND_GEN {
  FILE *dev;
  EVP_MD_CTX *md_ctx;
};

RAND_GEN *Rand_new(void)
{
  FILE *dev;
  EVP_MD_CTX *md_ctx;
  RAND_GEN *r;

  dev = fopen(RAND_DEV, "r");
  if (dev == NULL) {
    perror("error opening " RAND_DEV);
    return NULL;
  }

  md_ctx = EVP_MD_CTX_create();

  r = (RAND_GEN *)malloc(sizeof(RAND_GEN));
  if (r == NULL)
    return NULL;

  r->dev = dev;
  r->md_ctx = md_ctx;

  return r;
}

void Rand_delete(RAND_GEN *r)
{
  if (fclose(r->dev) == EOF) {
    perror("closing " RAND_DEV);
  }

  EVP_MD_CTX_destroy(r->md_ctx);

  free(r);
}

int Rand_long(RAND_GEN *r, unsigned long *pval)
{
  unsigned long val;
  unsigned char digest[EVP_MAX_MD_SIZE];
  int digest_len;

  if (fread(&val, sizeof(unsigned long), 1, r->dev) < 1) {
    perror("error reading " RAND_DEV);
    return -1;
  }

  EVP_DigestInit_ex(r->md_ctx, EVP_sha256(), NULL);
  EVP_DigestUpdate(r->md_ctx, &val, sizeof(unsigned long));
  EVP_DigestFinal_ex(r->md_ctx, digest, &digest_len);

  *pval = *(unsigned long *)digest;

  return 0;
}

int Rand_double(RAND_GEN *r, double *pval)
{
  unsigned long lval;

  if (Rand_long(r, &lval) != 0)
    return -1;

  *pval = ((double)lval) / ULONG_MAX;

  return 0;
}
